#pragma once

#ifdef GK_DEVICE_DIRECTX10

#include "..\GkContextInterface.h"
#include "GkRenderSurface.h"
#include "GkBuffer.h"
#include "GkGpuProgram.h"
#include "GkStreamDescription.h"
#include "GkGpuProgram.h"
#include "GkTexture.h"
#include "GkTypeConversion.DirectX10.h"

namespace gk
{
	struct ContextInternal : public ContextInterface
	{
		ID3D10Device*	m_pContext;
		RenderSurfaceHandle		m_hDepthSurface;
		RenderSurfaceHandle		m_hColourSurfaces[Constants::MaxColourTargets];
		RenderBindingHandle		m_hCurRenderBinding;
		ID3D10SamplerState*		m_pDummySamplerState;
		ID3D10ShaderResourceView*	s_nullShaderResourceView[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];

		ContextInternal()
		{
			memset(s_nullShaderResourceView, 0, sizeof(ID3D10ShaderResourceView*)*D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT);
		}

		void BeginFrame()
		{
		}

		void EndFrame()
		{
		}

		void SetRenderTarget(RenderTargetHandle hRenderTarget)
		{
			m_hDepthSurface = hRenderTarget->m_hDepthSurface;
			for (cr::size_t i = 0; i < Constants::MaxColourTargets; ++i)
			{
				m_hColourSurfaces[i] = hRenderTarget->m_hColourSurface[i];
			}

			ID3D10DepthStencilView* pDepthStencilView = m_hDepthSurface.IsValid() ? m_hDepthSurface->m_internal.m_pDepthStencilView : 0;
			ID3D10RenderTargetView* pRenderTargetViews[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT];
			cr::size_t numActiveColourTargets = 0;

			for (cr::size_t i = 0; i < D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
			{
				pRenderTargetViews[i] = 0;
				if (i < Constants::MaxColourTargets)
				{
					pRenderTargetViews[i] = m_hColourSurfaces[i].IsValid() ? m_hColourSurfaces[i]->m_internal.m_pRenderTargetView : 0;
					if (pRenderTargetViews[i])
						numActiveColourTargets++;
				}
			}

			m_pContext->OMSetRenderTargets(numActiveColourTargets, pRenderTargetViews, pDepthStencilView);
		}

		void ClearRenderTarget(ClearType::Enum clearType, float r, float g, float b, float a, float depth, cr::uint8_t stencil)
		{
			if (clearType & ClearType::DepthTarget && m_hDepthSurface.IsValid())
			{
				m_pContext->ClearDepthStencilView(m_hDepthSurface->m_internal.m_pDepthStencilView, 0, depth, stencil);
			}

			if (clearType & ClearType::ColourTarget)
			{
				FLOAT rgba[4] = {r,g,b,a};
				for (cr::size_t i = 0; i < Constants::MaxColourTargets; ++i)
				{
					if (m_hColourSurfaces[i].IsValid())
					{
						m_pContext->ClearRenderTargetView(m_hColourSurfaces[i]->m_internal.m_pRenderTargetView, rgba);
					}
				}				
			}
		}

		void SetRenderBinding(RenderBindingHandle hRenderBinding)
		{
			if (hRenderBinding.IsValid())
			{
				if (m_hCurRenderBinding.IsValid())
				{
					SetRenderBinding(NULL);
				}

				// Bind streams
				RenderBindingInternal& rInternal = hRenderBinding->m_internal;			

				BeginGpuProgram(hRenderBinding->m_hGpuProgram);

				m_pContext->IASetInputLayout(hRenderBinding->m_internal.m_pInputLayout);
				m_pContext->IASetVertexBuffers(0, rInternal.m_numVertexBuffers, rInternal.m_pVertexBuffers, rInternal.m_pStrides, rInternal.m_pOffsets);

				m_hCurRenderBinding = hRenderBinding;
			}
			else
			{
				if (m_hCurRenderBinding.IsValid())
				{
					EndGpuProgram();
					m_hCurRenderBinding = NULL;
				}
			}
		}

		void DrawIndexedPrimitives(BufferHandle hIndexBuffer, PrimitiveType::Enum primitiveType, cr::size_t indexCount, cr::size_t startIndex, cr::size_t vertexCount, cr::size_t startVertex)
		{
			m_pContext->IASetIndexBuffer(hIndexBuffer->m_internal.m_pBuffer, TypeConversion::ConvertBufferFormat(hIndexBuffer->m_format), startIndex);
			m_pContext->IASetPrimitiveTopology(TypeConversion::ConvertPrimitiveType(primitiveType));
			m_pContext->DrawIndexed(indexCount, 0, startVertex);
		}

		void DrawPrimitives(PrimitiveType::Enum primitiveType, cr::size_t startVertex, cr::size_t vertexCount)
		{
			m_pContext->IASetPrimitiveTopology(TypeConversion::ConvertPrimitiveType(primitiveType));
			m_pContext->Draw(vertexCount, startVertex);
		}

		void BeginGpuProgram(GpuProgramHandle hGpuProgram)
		{
			if (hGpuProgram->m_internal.m_pVertexShader)
				m_pContext->VSSetShader(hGpuProgram->m_internal.m_pVertexShader);

			if (hGpuProgram->m_internal.m_pPixelShader)
				m_pContext->PSSetShader(hGpuProgram->m_internal.m_pPixelShader);

			if (hGpuProgram->m_internal.m_pGeometryShader)
				m_pContext->GSSetShader(hGpuProgram->m_internal.m_pGeometryShader);
		}

		void EndGpuProgram()
		{
			m_pContext->PSSetShaderResources(0, D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, s_nullShaderResourceView);
			m_pContext->PSSetShader(NULL);
			m_pContext->VSSetShader(NULL);
			m_pContext->GSSetShader(NULL);
		}

// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, float v)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, float* v, cr::size_t numComponents)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, int v)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, int* v, cr::size_t numComponents)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, cr::Vec4fRef v)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, cr::Vec4f* v, cr::size_t numComponents)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, cr::Matrix4fRef v)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, cr::Matrix4f* v, cr::size_t numComponents)
// 		{
// 		}
// 
// 		void SetGpuProgramParameter(GpuProgramParameterHandle hParam, TextureHandle hTexture)
// 		{
// 		}

		void CommitConstantBuffer(ConstantBufferHandle hConstantBuffer)
		{
// 			switch (hConstantBuffer->m_target)
// 			{
// 			case ConstantBufferTarget::PixelSamplers:
// 				{
// 					m_pContext->PSSetShaderResources(0, hConstantBuffer->m_numSlots, hConstantBuffer->m_internal.m_ppTextures);
// 					m_pContext->PSSetSamplers(0, 1, &m_pDummySamplerState);
// 
// 					break;
// 				}
// 			case ConstantBufferTarget::VertexSamplers:
// 				{
// 					break;
// 				}
// 			case ConstantBufferTarget::PixelShader:
// 				{
// 					ID3D10Buffer* pBuffer = hConstantBuffer->m_internal.m_buffer.m_internal.m_pBuffer;
// 					m_pContext->UpdateSubresource(pBuffer, 0, NULL, hConstantBuffer->m_internal.m_pRaw, 0, 0 );
// 					m_pContext->PSSetConstantBuffers(0, 1, &hConstantBuffer->m_internal.m_buffer.m_internal.m_pBuffer);
// 					break;
// 				}
// 			case ConstantBufferTarget::VertexShader:
// 				{
// 					ID3D10Buffer* pBuffer = hConstantBuffer->m_internal.m_buffer.m_internal.m_pBuffer;
// 					m_pContext->UpdateSubresource(pBuffer, 0, NULL, hConstantBuffer->m_internal.m_pRaw, 0, 0 );
// 					m_pContext->VSSetConstantBuffers(0, 1, &hConstantBuffer->m_internal.m_buffer.m_internal.m_pBuffer);
// 					break;
// 				}
// 			}
		}

		void CommitGpuProgram()
		{
		}

		void CommitStateBlock(StateBlockHandle hStateBlock)
		{
		}
	};
}

#endif // GK_DEVICE_DIRECTX10